/*---------------------------------------------------------------------------*/
/*                                                                           */
/* CActiveSocket.cpp - Active Socket Implementation							 */
/*                                                                           */
/* Author : Mark Carrier (mark@carrierlabs.com)                              */
/*                                                                           */
/*---------------------------------------------------------------------------*/
#include "ActiveSocket.h"

#define GETHOSTBYNAME(a)       gethostbyname((const char *)a)
#define ACCEPT(a,b,c)          accept(a,b,c)
#define CONNECT(a,b,c)         connect(a,b,c)
#define CLOSE(a)               close(a)
#define READ(a,b,c)            read(a,b,c)
#define RECV(a,b,c,d)          recv(a, (void *)b, c, d)
#define RECVFROM(a,b,c,d,e,f)  recvfrom(a, (char *)b, c, d, (sockaddr *)e, f)
#define RECV_FLAGS             MSG_WAITALL
#define SELECT(a,b,c,d,e)      select(a,b,c,d,e)
#define SEND(a,b,c,d)          send(a, (const UDK_S8 *)b, c, d)
#define SENDTO(a,b,c,d,e,f)    sendto(a, (const UDK_S8 *)b, c, d, e, f)
#define SEND_FLAGS             0
#define SENDFILE(a,b,c,d)      sendfile(a, b, c, d)
#define SET_SOCKET_ERROR(x,y)  errno=y
#define SOCKET_ERROR_INTERUPT  EINTR
#define SOCKET_ERROR_TIMEDOUT  EAGAIN
#define WRITE(a,b,c)           write(a,b,c)
#define WRITEV(a,b,c)          writev(a, b, c)
#define GETSOCKOPT(a,b,c,d,e)  getsockopt((int)a,(int)b,(int)c,(void *)d,(socklen_t *)e)
#define SETSOCKOPT(a,b,c,d,e)  setsockopt((int)a,(int)b,(int)c,(const void *)d,(int)e)
#define GETHOSTBYNAME(a)       gethostbyname((const char *)a)
#define LSEEK(a,b,c)           lseek(a,b,c)

CActiveSocket::CActiveSocket(CSocketType nType) : CSimpleSocket(nType)
{
}


//------------------------------------------------------------------------------
//
// ConnectTCP() -
//
//------------------------------------------------------------------------------
bool CActiveSocket::ConnectTCP(const UDK_U8 *pAddr, UDK_U16 nPort)
{
    bool           bRetVal = false;
    struct in_addr stIpAddress;

    //------------------------------------------------------------------
    // Preconnection setup that must be preformed					 
    //------------------------------------------------------------------
    memset(&m_stServerSockaddr, 0, sizeof(m_stServerSockaddr));
    m_stServerSockaddr.sin_family = AF_INET;

    if ((m_pHE = GETHOSTBYNAME(pAddr)) == NULL) 
    {
        if (h_errno == HOST_NOT_FOUND)
        {
            SetSocketError(SocketInvalidAddress);
        }
        return bRetVal;
    }

    memcpy(&stIpAddress, m_pHE->h_addr_list[0], m_pHE->h_length);
    m_stServerSockaddr.sin_addr.s_addr = stIpAddress.s_addr;

    if ((UDK_S32)m_stServerSockaddr.sin_addr.s_addr == CSimpleSocket::SocketError)
    {
        TranslateSocketError();
        return bRetVal;
    }

    m_stServerSockaddr.sin_port = htons(nPort);

    //------------------------------------------------------------------
    // Connect to address "xxx.xxx.xxx.xxx"	(IPv4) address only.  
    // 
    //------------------------------------------------------------------
    m_timer.Initialize();
    m_timer.SetStartTime();

    if (connect(m_socket, (struct sockaddr*)&m_stServerSockaddr, sizeof(m_stServerSockaddr)) == 
	CSimpleSocket::SocketError)
    {
        //--------------------------------------------------------------
        // Get error value this might be a non-blocking socket so we 
        // must first check.
        //--------------------------------------------------------------
        TranslateSocketError();

        //--------------------------------------------------------------
        // If the socket is non-blocking and the current socket error
        // is SocketEinprogress or SocketEwouldblock then poll connection 
        // with select for designated timeout period.
        // Linux returns EINPROGRESS and Windows returns WSAEWOULDBLOCK.
        //--------------------------------------------------------------
        if ((IsNonblocking()) && 
            ((GetSocketError() == CSimpleSocket::SocketEwouldblock) || 
	     (GetSocketError() == CSimpleSocket::SocketEinprogress)))
        {
            bRetVal = Select(GetConnectTimeoutSec(), GetConnectTimeoutUSec());
        }
    }
    else
    {
        bRetVal = true;
    }

    m_timer.SetEndTime();

    return bRetVal;
}


//------------------------------------------------------------------------------
//
// ConnectUDP() -
//
//------------------------------------------------------------------------------
bool CActiveSocket::ConnectUDP(const UDK_U8 *pAddr, UDK_U16 nPort)
{
    bool           bRetVal = false;
    struct in_addr stIpAddress;
    //------------------------------------------------------------------
    // Pre-connection setup that must be preformed					 
    //------------------------------------------------------------------
    memset(&m_stServerSockaddr, 0, sizeof(m_stServerSockaddr));
    m_stServerSockaddr.sin_family = AF_INET;

   
    if ((m_pHE = GETHOSTBYNAME(pAddr)) == NULL) 
    {
        if (h_errno == HOST_NOT_FOUND)
        {
            SetSocketError(SocketInvalidAddress);
        }
        return bRetVal;
    }

    memcpy(&stIpAddress, m_pHE->h_addr_list[0], m_pHE->h_length);
    m_stServerSockaddr.sin_addr.s_addr = stIpAddress.s_addr;
    m_stServerSockaddr.sin_port = htons(nPort);

    //------------------------------------------------------------------
    // Connect to address "xxx.xxx.xxx.xxx"	(IPv4) address only.  
    // 
    //------------------------------------------------------------------
    m_timer.Initialize();
    m_timer.SetStartTime();

    if(isBroadcastAddress((const char*)pAddr)){
	bRetVal = SetBroadcast(true); 		
    }else if(isMulticastAddress((const char*)pAddr)){
	bRetVal = SetMulticast(true);
    }else {
	    if ((UDK_S32)m_stServerSockaddr.sin_addr.s_addr == CSimpleSocket::SocketError)
	    {
	        TranslateSocketError();
	        return bRetVal;
	    }

	    //only unicast address can connect to .
	    if (connect(m_socket, (struct sockaddr*)&m_stServerSockaddr, sizeof(m_stServerSockaddr)) != 		CSimpleSocket::SocketError){			
		        bRetVal = true;
    		}
    }

    m_timer.SetEndTime();

    return bRetVal;
}


//------------------------------------------------------------------------------
//
// ConnectRAW() -
//
//------------------------------------------------------------------------------
bool CActiveSocket::ConnectRAW(const UDK_U8 *pAddr, UDK_U16 nPort)
{
    bool           bRetVal = false;
    struct in_addr stIpAddress;
    //------------------------------------------------------------------
    // Pre-connection setup that must be preformed					 
    //------------------------------------------------------------------
    memset(&m_stServerSockaddr, 0, sizeof(m_stServerSockaddr));
    m_stServerSockaddr.sin_family = AF_INET;

    if ((m_pHE = GETHOSTBYNAME(pAddr)) == NULL) 
    {
        if (h_errno == HOST_NOT_FOUND)
        {
            SetSocketError(SocketInvalidAddress);
        }
        return bRetVal;
    }

    memcpy(&stIpAddress, m_pHE->h_addr_list[0], m_pHE->h_length);
    m_stServerSockaddr.sin_addr.s_addr = stIpAddress.s_addr;

    if ((UDK_S32)m_stServerSockaddr.sin_addr.s_addr == CSimpleSocket::SocketError)
    {
        TranslateSocketError();
        return bRetVal;
    }

    m_stServerSockaddr.sin_port = htons(nPort);

    //------------------------------------------------------------------
    // Connect to address "xxx.xxx.xxx.xxx"	(IPv4) address only.  
    // 
    //------------------------------------------------------------------
    m_timer.Initialize();
    m_timer.SetStartTime();

    if (connect(m_socket, (struct sockaddr*)&m_stServerSockaddr, sizeof(m_stServerSockaddr)) != CSimpleSocket::SocketError)
    {
        bRetVal = true;
    }

    TranslateSocketError();

    m_timer.SetEndTime();

    return bRetVal;
}




//------------------------------------------------------------------------------
//
// Open() - Create a connection to a specified address on a specified port
//
//------------------------------------------------------------------------------
bool CActiveSocket::Open(const UDK_U8 *pAddr, UDK_U16 nPort)
{
    bool bRetVal = false;

    if (IsSocketValid() == false)
    {
        SetSocketError(CSimpleSocket::SocketInvalidSocket);
        return bRetVal;
    }

    if (pAddr == NULL)
    {
        SetSocketError(CSimpleSocket::SocketInvalidAddress);
        return bRetVal;
    }

    if (nPort == 0)
    {
        SetSocketError(CSimpleSocket::SocketInvalidPort);
        return bRetVal;
    }

    switch (m_nSocketType)
    {
        case CSimpleSocket::SocketTypeTcp :
        {
            bRetVal = ConnectTCP(pAddr, nPort);
            break;
        }
        case CSimpleSocket::SocketTypeUdp :
        {
            bRetVal = ConnectUDP(pAddr, nPort);
            break;
        }
        case CSimpleSocket::SocketTypeRaw :
            break;
        default:
            break;
    }

    //--------------------------------------------------------------------------
    // If successful then create a local copy of the address and port							
    //--------------------------------------------------------------------------
    if (bRetVal)
    {
        socklen_t nSockLen = sizeof(struct sockaddr);
	
	   if((false==isBroadcastAddress((const char*)pAddr))&&(false==isMulticastAddress((const char*)pAddr))){		
		memset(&m_stServerSockaddr, 0, nSockLen);
		getpeername(m_socket, (struct sockaddr *)&m_stServerSockaddr, &nSockLen);
	   }
        nSockLen = sizeof(struct sockaddr);
        memset(&m_stClientSockaddr, 0, nSockLen);
        getsockname(m_socket, (struct sockaddr *)&m_stClientSockaddr, &nSockLen);
        if(true==isBroadcastAddress((const char*)pAddr)){
        	int nReuse=1;
        	m_stClientSockaddr.sin_family=AF_INET;
          	m_stClientSockaddr.sin_addr.s_addr = htonl(INADDR_ANY);
        	m_stClientSockaddr.sin_port        = htons(0);        	
        	SETSOCKOPT(m_socket, SOL_SOCKET, SO_REUSEADDR, (char*)&nReuse, sizeof(nReuse));
          	int ret = bind(m_socket,(struct sockaddr *)&m_stClientSockaddr, sizeof(m_stClientSockaddr));
        	if(CSimpleSocket::SocketError==ret){
        		UDK_LOGE("socket binding error %d %d\n",ret,errno);        		
        		bRetVal=false;
        	}
        	//UDK_LOGW("client %s@%d bind ret=%d\n",inet_ntoa(m_stClientSockaddr.sin_addr),m_stClientSockaddr.sin_port,ret);
        }
        	
        SetSocketError(SocketSuccess);	
    }
	

    return bRetVal;
}
